package com.jmu.medicineknows.service.impl;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.jmu.medicineknows.config.RedisCache;
import com.jmu.medicineknows.dto.UserListDto;
import com.jmu.medicineknows.entity.*;
import com.jmu.medicineknows.mapper.UserMapper;
import com.jmu.medicineknows.service.UserService;
import com.jmu.medicineknows.utils.JwtUtil;
import com.jmu.medicineknows.utils.MenuUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.util.*;

/**
 * @author Administrator
 */
@Service
@SuppressWarnings("all")
public class UserServiceImpl implements UserService {

    @Autowired
    private AuthenticationManager authenticationManager;

    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
    SimpleDateFormat sdfDae = new SimpleDateFormat("yyyy-MM-dd");
    @Autowired
    private RedisCache redisCache;

    @Autowired
    private UserMapper userMapper;
    private static final String DAU_COUNT_PREFIX = "dau:";
    private static final String DAU_TODAY_USER = "users";
    private static final String DAU_TODAY = "dau";
    public static final String PV_KEY = "pv";
    public static final String PV_PREFIX = "pv:";

    @Override
    public Result login(User user) {
        UsernamePasswordAuthenticationToken authenticationToken =
                new UsernamePasswordAuthenticationToken(user.getUserName(), user.getPassword());
        Authentication authentication = authenticationManager.authenticate(authenticationToken);

        if (Objects.isNull(authentication)) {
            throw new RuntimeException("登录失败");
        }
        LoginUser loginUser = (LoginUser) authentication.getPrincipal();
        char status = loginUser.getUser().getStatus();
        if(status == '1'){
            return Result.failure(405,"账户已停用，请联系管理员");
        }
        String id = loginUser.getUser().getId().toString();
        String jwt = JwtUtil.createJWT(id);
        redisCache.setCacheObject("login:" + id, loginUser);
        //返回token和用户信息
        Map<String, String> map = new HashMap<>();
        map.put("token", jwt);
        map.put("UserInfo", JSON.toJSONString(loginUser.getUser()));
        //DAU统计
        String date = sdfDae.format(new Date());
        String key = DAU_COUNT_PREFIX + date;
        if (!redisCache.existsKey(key)) {
            Map<String, Object> dauMap = new HashMap<>();
            Set<String> userSet = new HashSet<>();
            dauMap.put(DAU_TODAY_USER, userSet);
            userSet.add(id);
            int initDau = 1;
            dauMap.put(DAU_TODAY, initDau);
            redisCache.setCacheMap(key, dauMap);
        } else {
            Map<String, Object> dauMap = redisCache.getCacheMap(key);
            Set<String> set = (Set<String>) dauMap.get(DAU_TODAY_USER);
            if (!set.contains(id)) {
                Integer dau = (Integer) dauMap.get(DAU_TODAY);
                dauMap.put(DAU_TODAY, dau + 1);
                set.add(id);
                dauMap.put(DAU_TODAY_USER, set);
            }
            redisCache.setCacheMap(key, dauMap);
        }
        return Result.defaultSuccess(map);
    }

    @Override
    public Result navData() {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        LoginUser loginUser = (LoginUser) authentication.getPrincipal();
        List<Menu> menus = userMapper.selectMenuByUserId(loginUser.getUser().getId());
        List<Menu> menuV0List = MenuUtil.generateMenu(menus);
        return Result.defaultSuccess(menuV0List);
    }

    @Override
    public Result register(User user) {
        BCryptPasswordEncoder bCryptPasswordEncoder = new BCryptPasswordEncoder();
        String newPassword = bCryptPasswordEncoder.encode(user.getPassword().trim());
        user.setPassword(newPassword);
        user.setUserType('2');
        user.setStatus('0');
        user.setCreateTime(sdf.format(new Date()));
        user.setAvatar("default.jpg");
        int cnt = userMapper.insert(user);
        userMapper.insertUserRole(user);
        if (cnt > 0) {
            return Result.defaultSuccess("");
        }
        return Result.defaultFailure();
    }

    @Override
    public Result upload(MultipartFile file) {
        Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
        LoginUser loginUser = (LoginUser) authentication.getPrincipal();
        User user = loginUser.getUser();
        String filename = System.currentTimeMillis() + new Random().nextInt(10) +
                file.getOriginalFilename().substring(file.getOriginalFilename().indexOf("."));
        String rootPath = "";
        String osName = System.getProperty("os.name");
        if ("linux".equals(osName) || "mac".equals(osName)) {
            rootPath = "/root/uploads";
        } else {
            rootPath = "D://uploads";
        }
        File uploadDir = new File(rootPath);
        if (!uploadDir.exists()) {
            uploadDir.mkdirs();
        }
        String imagePath = rootPath + File.separator + filename;
        try {
            user.setAvatar(filename);
            file.transferTo(new File(imagePath));
            LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper();
            queryWrapper.eq(User::getId, user.getId());
            int cnt = userMapper.update(user, queryWrapper);
            if (cnt <= 0) {
                return Result.defaultFailure();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return Result.defaultSuccess(filename);
    }

    @Override
    public Result update(User user) {
        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
        user.setUpdateTime(sdf.format(new Date()));
        queryWrapper.eq(User::getId, user.getId());
        if(user.getStatus() == '0'){
            user.setDelFlag(0);
        }
        User selectOne = userMapper.selectOne(queryWrapper);
        if(selectOne.getUserType() != user.getUserType()){
            UserRole userRole = new UserRole();
            char type = user.getUserType();
            userRole.setUserId(user.getId());
            userRole.setRoleId(Long.parseLong(String.valueOf(type - '0')));
            userMapper.updateUserRole(userRole);
        }
        return Result.defaultSuccess(userMapper.update(user, queryWrapper));
    }

    @Override
    public Result logout() {
        UsernamePasswordAuthenticationToken authenticationToken = (UsernamePasswordAuthenticationToken) SecurityContextHolder.getContext().getAuthentication();
        LoginUser loginUser = (LoginUser) authenticationToken.getPrincipal();
        Long id = loginUser.getUser().getId();

        String key = "login:" + id;
        redisCache.deleteObject(key);
        return Result.newInstance().code(200)
                .sucess(true).message("注销成功");
    }

    @Override
    public Result getDau() {
        String key = DAU_COUNT_PREFIX + sdfDae.format(new Date());
        if (redisCache.existsKey(key)) {
            Map<String, Object> dauMap = redisCache.getCacheMap(key);
            Map<String, Integer> resultMap = new HashMap<>();
            String today = sdfDae.format(new Date());
            Integer todayCount = (Integer) dauMap.get(DAU_TODAY);
            resultMap.put("today", todayCount);
            // 获取当前日期前一天的日期
            LocalDate localDate = LocalDate.now().minusDays(1);
            String yesKey = DAU_COUNT_PREFIX + localDate.toString();
            if (dauMap.containsKey(yesKey)) {
                Integer yesCount = (Integer) dauMap.get(DAU_TODAY);
                resultMap.put("yesterday", yesCount);
            } else {
                resultMap.put("yesterday", 0);
            }
            return Result.defaultSuccess(resultMap);
        }
        return Result.defaultFailure();
    }

    @Override
    public Result getDauByDate(String date) {
        String key = DAU_COUNT_PREFIX + sdfDae.format(new Date());
        if (redisCache.existsKey(key)) {
            String hkey = DAU_COUNT_PREFIX + sdfDae.format(date);
            Integer dauCount = (Integer) redisCache.getCacheMapValue(DAU_TODAY, hkey);
            return Result.defaultSuccess(dauCount);
        }
        return Result.defaultFailure();
    }

    @Override
    public Result userCount() {
        return Result.defaultSuccess(userMapper.selectCount(null));
    }

    @Override
    public Result pvCount() {
        String key = PV_PREFIX + sdfDae.format(new Date());
        Map<String, Integer> resultMap = new HashMap<>();
        if (redisCache.existsKey(PV_KEY)) {
            Integer todayPv = redisCache.getCacheMapValue(PV_KEY, key);
            LocalDate localDate = LocalDate.now().minusDays(1);
            String yesKey = PV_PREFIX + localDate.toString();
            Integer yesPv = redisCache.getCacheMapValue(PV_KEY, yesKey);
            Integer totalPv = redisCache.getCacheMapValue(PV_KEY, "totalPv");
            resultMap.put("pv", todayPv);
            resultMap.put("yesPv", yesPv == null ? 0 : yesPv);
            resultMap.put("totalPv", totalPv);
            return Result.defaultSuccess(resultMap);
        }
        return Result.defaultFailure();
    }

    @Override
    public Result binding(User user) {
        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(User::getSecurityNum, user.getSecurityNum())
                .ne(User::getId, user.getId());
        User selected = userMapper.selectOne(queryWrapper);
        if (!Objects.isNull(selected)) {
            return Result.newInstance().sucess(true).code(200).message("卡号重复");
        }
        UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();
        updateWrapper.set("security_num", user.getSecurityNum());
        updateWrapper.eq("id", user.getId());
        return Result.defaultSuccess(userMapper.update(null, updateWrapper));
    }

    @Override
    public Result checkBinding(User user) {
        if (user.getId() == null) {
            return Result.defaultSuccess(0);
        }
        LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.eq(User::getId, user.getId());
        User selected = userMapper.selectOne(queryWrapper);
        String securityNum = selected.getSecurityNum();
        if (securityNum.equals(user.getSecurityNum())) {
            return Result.defaultSuccess(1);
        }
        return Result.defaultSuccess(0);
    }

    @Override
    public Result userList(UserListDto dto) {
        IPage<User> page = new Page<>(dto.currentPage, dto.pageSize);
        LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        if(!Objects.isNull(dto.id)){
            lambdaQueryWrapper.eq(User::getId,dto.id);
        }
        if(StringUtils.hasText(dto.start) && StringUtils.hasText(dto.end)){
            lambdaQueryWrapper.between(User::getCreateTime,dto.start,dto.end);
        }else if(StringUtils.hasText(dto.start)){
            lambdaQueryWrapper.le(User::getCreateTime,dto.start);
        }else if(StringUtils.hasText(dto.end)){
            lambdaQueryWrapper.le(User::getCreateTime,dto.end);
        }
        lambdaQueryWrapper.eq(User::getUserType, "2");
        return Result.defaultSuccess(userMapper.selectPage(page, lambdaQueryWrapper));
    }

    @Override
    public Result delete(Long id) {
        UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();
        updateWrapper.set("del_flag", 1);
        updateWrapper.set("status", "1");
        updateWrapper.eq("id", id);
        return Result.defaultSuccess(userMapper.update(null, updateWrapper));
    }
}
